home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / hplip / hpssd.py < prev    next >
Text File  |  2009-10-09  |  19KB  |  557 lines

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. #
  4. # (c) Copyright 2003-2009 Hewlett-Packard Development Company, L.P.
  5. #
  6. # This program is free software; you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation; either version 2 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program; if not, write to the Free Software
  18. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  19. #
  20. # Author: Don Welch
  21. #
  22.  
  23. __version__ = '12.0'
  24. __title__ = "Services and Status System Tray dBus Child/Parent Process"
  25. __mod__ = 'hpssd'
  26. __doc__ = "Provides persistent data and event services to HPLIP client applications. Required to be running for PC send fax, optional in all other cases."
  27.  
  28.  
  29. # StdLib
  30. import sys
  31. import struct
  32. import os
  33. import time
  34. import getopt
  35. import select
  36. import signal
  37. import tempfile
  38. #import threading
  39. #import Queue
  40. from cPickle import loads, HIGHEST_PROTOCOL
  41.  
  42. # Local
  43. from base.g import *
  44. from base.codes import *
  45. from base import utils, device, status, models
  46.  
  47. # dBus
  48. try:
  49.     from dbus import lowlevel, SystemBus, SessionBus
  50.     import dbus.service
  51.     from dbus.mainloop.glib import DBusGMainLoop
  52.     from gobject import MainLoop, timeout_add, threads_init, io_add_watch, IO_IN
  53.     dbus_loaded = True
  54. except ImportError:
  55.     log.error("dbus failed to load (python-dbus ver. 0.80+ required). Exiting...")
  56.     dbus_loaded = False
  57.     sys.exit(1)
  58.  
  59. import warnings
  60. # Ignore: .../dbus/connection.py:242: DeprecationWarning: object.__init__() takes no parameters
  61. # (occurring on Python 2.6/dBus 0.83/Ubuntu 9.04)
  62. warnings.simplefilter("ignore", DeprecationWarning)
  63.  
  64.  
  65. # Globals
  66. PIPE_BUF = 4096
  67. dbus_loop, main_loop = None, None
  68. system_bus = None
  69. session_bus = None
  70. w1, w2, r3 = None, None, None
  71. devices = {} # { 'device_uri' : DeviceCache, ... }
  72.  
  73.  
  74. # ***********************************************************************************
  75. #
  76. # DEVICE CACHE
  77. #
  78. # ***********************************************************************************
  79.  
  80. class DeviceCache(object):
  81.     def __init__(self, model=''):
  82.         self.history = utils.RingBuffer(prop.history_size) # circular buffer of device.Event
  83.         self.model = models.normalizeModelName(model)
  84.         self.cache = {} # variable name : value
  85.         self.faxes = {} # (username, jobid): FaxEvent
  86.         self.dq = {} # last device query results
  87.         #self.backoff = False
  88.         self.backoff_counter = 0  # polling backoff: 0 = none, x = backed off by x intervals
  89.         self.backoff_countdown = 0
  90.         self.polling = False # indicates whether its in the device polling list
  91.  
  92.  
  93. #  dbus interface on session bus
  94. class StatusService(dbus.service.Object):
  95.     def __init__(self, name, object_path):
  96.         dbus.service.Object.__init__(self, name, object_path)
  97.  
  98.  
  99.     @dbus.service.method('com.hplip.StatusService', in_signature='s', out_signature='sa(ssisisd)')
  100.     def GetHistory(self, device_uri):
  101.         log.debug("GetHistory('%s')" % device_uri)
  102.         send_systray_blip()
  103.         try:
  104.             devices[device_uri]
  105.         except KeyError:
  106.             #log.warn("Unknown device URI: %s" % device_uri)
  107.             return (device_uri, [])
  108.         else:
  109.             h = devices[device_uri].history.get()
  110.             log.debug("%d events in history:" % len(h))
  111.             [x.debug() for x in h]
  112.             return (device_uri, [x.as_tuple() for x in h])
  113.  
  114.  
  115.     @dbus.service.method('com.hplip.StatusService', in_signature='s', out_signature='sa{ss}')
  116.     def GetStatus(self, device_uri):
  117.         log.debug("GetStatus('%s')" % device_uri)
  118.         send_systray_blip()
  119.         try:
  120.             devices[device_uri]
  121.         except KeyError:
  122.             #log.warn("Unknown device URI: %s" % device_uri)
  123.             return (device_uri, {})
  124.         else:
  125.             t = {}
  126.             dq = devices[device_uri].dq
  127.             [t.setdefault(x, str(dq[x])) for x in dq.keys()]
  128.             log.debug(t)
  129.             return (device_uri, t)
  130.  
  131.  
  132.     @dbus.service.method('com.hplip.StatusService', in_signature='ssi', out_signature='i')
  133.     def SetCachedIntValue(self, device_uri, key, value):
  134.         log.debug("SetCachedIntValue('%s', '%s', %d)" % (device_uri, key, value))
  135.         if check_device(device_uri) == ERROR_SUCCESS:
  136.             devices[device_uri].cache[key] = value
  137.             return value
  138.  
  139.         return -1
  140.  
  141.  
  142.     @dbus.service.method('com.hplip.StatusService', in_signature='ss', out_signature='i')
  143.     def GetCachedIntValue(self, device_uri, key):
  144.         try:
  145.             ret = devices[device_uri].cache[key]
  146.         except KeyError:
  147.             ret = -1
  148.  
  149.         log.debug("GetCachedIntValue('%s', '%s') --> %d" % (device_uri, key, ret))
  150.         return ret
  151.  
  152.  
  153.     @dbus.service.method('com.hplip.StatusService', in_signature='sss', out_signature='s')
  154.     def SetCachedStrValue(self, device_uri, key, value):
  155.         log.debug("SetCachedStrValue('%s', '%s', '%s')" % (device_uri, key, value))
  156.         if check_device(device_uri) == ERROR_SUCCESS:
  157.             devices[device_uri].cache[key] = value
  158.             return value
  159.  
  160.         return ''
  161.  
  162.  
  163.     @dbus.service.method('com.hplip.StatusService', in_signature='ss', out_signature='s')
  164.     def GetCachedStrValue(self, device_uri, key):
  165.         try:
  166.             ret = devices[device_uri].cache[key]
  167.         except KeyError:
  168.             ret = ''
  169.  
  170.         log.debug("GetCachedStrValue('%s', '%s') --> %s" % (device_uri, key, ret))
  171.         return ret
  172.  
  173.  
  174.     # Pass a non-zero job_id to retrieve a specific fax
  175.     # Pass zero for job_id to retrieve any avail. fax
  176.     @dbus.service.method('com.hplip.StatusService', in_signature='ssi', out_signature='ssisisds')
  177.     def CheckForWaitingFax(self, device_uri, username, job_id=0):
  178.         log.debug("CheckForWaitingFax('%s', '%s', %d)" % (device_uri, username, job_id))
  179.         send_systray_blip()
  180.         r = (device_uri, '', 0, username, job_id, '', 0.0, '')
  181.         check_device(device_uri)
  182.         show_waiting_faxes(device_uri)
  183.  
  184.         if job_id: # check for specific job_id
  185.             try:
  186.                 devices[device_uri].faxes[(username, job_id)]
  187.             except KeyError:
  188.                 return r
  189.             else:
  190.                 return self.check_for_waiting_fax_return(device_uri, username, job_id)
  191.  
  192.         else: # return any matching one from cache. call mult. times to get all.
  193.             for u, j in devices[device_uri].faxes.keys():
  194.                 if u == username:
  195.                     return self.check_for_waiting_fax_return(device_uri, u, j)
  196.  
  197.             return r
  198.  
  199.  
  200.     # if CheckForWaitingFax returns a fax job, that job is removed from the cache
  201.     def check_for_waiting_fax_return(self, d, u, j):
  202.         log.debug("Fax (username=%s, jobid=%d) removed from faxes and returned to caller." % (u, j))
  203.         r = devices[d].faxes[(u, j)].as_tuple()
  204.         del devices[d].faxes[(u, j)]
  205.         show_waiting_faxes(d)
  206.         return r
  207.  
  208.  
  209.     # Alternate way to "send" an event rather than using a signal message
  210.     @dbus.service.method('com.hplip.StatusService', in_signature='ssisis', out_signature='')
  211.     def SendEvent(self, device_uri, printer_name, event_code, username, job_id, title):
  212.         event = device.Event(device_uri, printer_name, event_code, username, job_id, title)
  213.         handle_event(event)
  214.  
  215.  
  216.  
  217. def check_device(device_uri):
  218.     try:
  219.         devices[device_uri]
  220.     except KeyError:
  221.         log.debug("New device: %s" % device_uri)
  222.         try:
  223.             back_end, is_hp, bus, model, serial, dev_file, host, zc, port = \
  224.                 device.parseDeviceURI(device_uri)
  225.         except Error:
  226.             log.debug("Invalid device URI: %s" % device_uri)
  227.             return ERROR_INVALID_DEVICE_URI
  228.  
  229.         devices[device_uri] = DeviceCache(model)
  230.  
  231.     return ERROR_SUCCESS
  232.  
  233.  
  234. def create_history(event):
  235.     history = devices[event.device_uri].history.get()
  236.  
  237.     if history and history[-1].event_code == event.event_code:
  238.         log.debug("Duplicate event. Replacing previous event.")
  239.         devices[event.device_uri].history.replace(event)
  240.         return True
  241.     else:
  242.         devices[event.device_uri].history.append(event)
  243.         return False
  244.  
  245.  
  246.  
  247. def handle_fax_event(event, pipe_name):
  248.     if event.event_code == EVENT_FAX_RENDER_COMPLETE and \
  249.         event.username == prop.username:
  250.  
  251.         fax_file_fd, fax_file_name = tempfile.mkstemp(prefix="hpfax-")
  252.         pipe = os.open(pipe_name, os.O_RDONLY)
  253.         bytes_read = 0
  254.         while True:
  255.             data = os.read(pipe, PIPE_BUF)
  256.             if not data:
  257.                 break
  258.  
  259.             os.write(fax_file_fd, data)
  260.             bytes_read += len(data)
  261.  
  262.         log.debug("Saved %d bytes to file %s" % (bytes_read, fax_file_name))
  263.  
  264.         os.close(pipe)
  265.         os.close(fax_file_fd)
  266.  
  267.         devices[event.device_uri].faxes[(event.username, event.job_id)] = \
  268.             device.FaxEvent(fax_file_name, event)
  269.  
  270.         show_waiting_faxes(event.device_uri)
  271.  
  272.         try:
  273.             os.waitpid(-1, os.WNOHANG)
  274.         except OSError:
  275.             pass
  276.  
  277.         # See if hp-sendfax is already running for this queue
  278.         ok, lock_file = utils.lock_app('hp-sendfax-%s' % event.printer_name, True)
  279.  
  280.         if ok:
  281.             # able to lock, not running...
  282.             utils.unlock(lock_file)
  283.  
  284.             path = utils.which('hp-sendfax')
  285.             if path:
  286.                 path = os.path.join(path, 'hp-sendfax')
  287.             else:
  288.                 log.error("Unable to find hp-sendfax on PATH.")
  289.                 return
  290.  
  291.             log.debug("Running hp-sendfax: %s --printer=%s" % (path, event.printer_name))
  292.             os.spawnlp(os.P_NOWAIT, path, 'hp-sendfax',
  293.                 '--printer=%s' % event.printer_name)
  294.  
  295.         else:
  296.             # cannot lock file - hp-sendfax is running
  297.             # no need to do anything... hp-sendfax is polling
  298.             log.debug("hp-sendfax is running. Waiting for CheckForWaitingFax() call.")
  299.  
  300.     else:
  301.         log.warn("Not handled!")
  302.         pass
  303.  
  304.  
  305. def show_waiting_faxes(d):
  306.     f = devices[d].faxes
  307.  
  308.     if not len(f):
  309.         log.debug("No faxes waiting for %s" % d)
  310.     else:
  311.         if len(f) == 1:
  312.             log.debug("1 fax waiting for %s:" % d)
  313.         else:
  314.             log.debug("%d faxes waiting for %s:" % (len(f), d))
  315.  
  316.         [f[x].debug() for x in f]
  317.  
  318.  
  319. # Qt4 only
  320. def handle_hpdio_event(event, bytes_written):
  321.     log.debug("Reading %d bytes from hpdio pipe..." % bytes_written)
  322.     total_read, data = 0, ''
  323.  
  324.     while True:
  325.         r, w, e = select.select([r3], [], [r3], 0.0)
  326.         if not r: break
  327.  
  328.         x = os.read(r3, PIPE_BUF)
  329.         if not x: break
  330.  
  331.         data = ''.join([data, x])
  332.         total_read += len(x)
  333.  
  334.         if total_read == bytes_written: break
  335.  
  336.     log.debug("Read %d bytes" % total_read)
  337.  
  338.     if total_read == bytes_written:
  339.         dq = loads(data)
  340.  
  341.         if check_device(event.device_uri) == ERROR_SUCCESS:
  342.             devices[event.device_uri].dq = dq.copy()
  343.  
  344.             handle_event(device.Event(event.device_uri, '',
  345.                 dq.get('status-code', STATUS_PRINTER_IDLE), prop.username, 0, ''))
  346.  
  347.             send_toolbox_event(event, EVENT_DEVICE_UPDATE_REPLY)
  348.  
  349.  
  350.  
  351. def handle_event(event, more_args=None):
  352.     #global polling_blocked
  353.     #global request_queue
  354.  
  355.     log.debug("Handling event...")
  356.  
  357.     if more_args is None:
  358.         more_args = []
  359.  
  360.     event.debug()
  361.  
  362.     if event.device_uri and check_device(event.device_uri) != ERROR_SUCCESS:
  363.         return
  364.  
  365.     # If event-code > 10001, its a PJL error code, so convert it
  366.     if event.event_code > EVENT_MAX_EVENT:
  367.         event.event_code = status.MapPJLErrorCode(event.event_code)
  368.  
  369.     # regular user/device status event
  370.     if event.event_code < EVENT_MIN_USER_EVENT:
  371.         pass
  372.  
  373.     elif EVENT_MIN_USER_EVENT <= event.event_code <= EVENT_MAX_USER_EVENT:
  374.  
  375.         if event.device_uri:
  376.             #event.device_uri = event.device_uri.replace('hpfax:', 'hp:')
  377.             dup_event = create_history(event)
  378.  
  379.             if event.event_code in (EVENT_DEVICE_STOP_POLLING,
  380.                                     EVENT_START_MAINT_JOB,
  381.                                     EVENT_START_COPY_JOB,
  382.                                     EVENT_START_FAX_JOB,
  383.                                     EVENT_START_PRINT_JOB):
  384.                 pass # stop polling (increment counter)
  385.  
  386.             elif event.event_code in (EVENT_DEVICE_START_POLLING, # should this event force counter to 0?
  387.                                       EVENT_END_MAINT_JOB,
  388.                                       EVENT_END_COPY_JOB,
  389.                                       EVENT_END_FAX_JOB,
  390.                                       EVENT_END_PRINT_JOB,
  391.                                       EVENT_PRINT_FAILED_MISSING_PLUGIN,
  392.                                       EVENT_SCANNER_FAIL,
  393.                                       EVENT_END_SCAN_JOB,
  394.                                       EVENT_SCAN_FAILED_MISSING_PLUGIN,
  395.                                       EVENT_FAX_JOB_FAIL,
  396.                                       EVENT_FAX_JOB_CANCELED,
  397.                                       EVENT_FAX_FAILED_MISSING_PLUGIN,
  398.                                       EVENT_COPY_JOB_FAIL,
  399.                                       EVENT_COPY_JOB_CANCELED):
  400.                 pass # start polling if counter <= 0
  401.                 # TODO: Do tools send END event if canceled or failed? Should they?
  402.                 # TODO: What to do if counter doesn't hit 0 after a period? Timeout?
  403.                 # TODO: Also, need to deal with the backoff setting (or it completely sep?)
  404.  
  405.         # Send to system tray icon if available
  406.         if not dup_event: # and event.event_code != STATUS_PRINTER_IDLE:
  407.             send_event_to_systray_ui(event)
  408.  
  409.         # send EVENT_HISTORY_UPDATE signal to hp-toolbox
  410.         send_toolbox_event(event, EVENT_HISTORY_UPDATE)
  411.  
  412.     # Handle fax signals
  413.     elif EVENT_FAX_MIN <= event.event_code <= EVENT_FAX_MAX and more_args:
  414.         log.debug("Fax event")
  415.         pipe_name = str(more_args[0])
  416.         handle_fax_event(event, pipe_name)
  417.  
  418.     elif event.event_code == EVENT_USER_CONFIGURATION_CHANGED:
  419.         # Sent if polling, hiding, etc. configuration has changed
  420.     #    send_event_to_hpdio(event)
  421.         send_event_to_systray_ui(event)
  422.  
  423.     elif event.event_code == EVENT_SYS_CONFIGURATION_CHANGED: # Not implemented
  424.         #send_event_to_hpdio(event)
  425.         send_event_to_systray_ui(event)
  426.  
  427.     # Qt4 only
  428.     elif event.event_code in (EVENT_DEVICE_UPDATE_REQUESTED,):
  429.                               #EVENT_DEVICE_START_POLLING,  # ?  Who handles polling? hpssd? probably...
  430.                               #EVENT_DEVICE_STOP_POLLING):  # ?
  431.         send_event_to_hpdio(event)
  432.  
  433.     # Qt4 only
  434.     elif event.event_code in (EVENT_DEVICE_UPDATE_ACTIVE,
  435.                               EVENT_DEVICE_UPDATE_INACTIVE):
  436.         send_event_to_systray_ui(event)
  437.  
  438.     # Qt4 only
  439.     elif event.event_code == EVENT_DEVICE_UPDATE_REPLY:
  440.         bytes_written = int(more_args[1])
  441.         handle_hpdio_event(event, bytes_written)
  442.  
  443.     # Qt4 only
  444.     elif event.event_code == EVENT_SYSTEMTRAY_EXIT:
  445.         send_event_to_hpdio(event)
  446.         send_toolbox_event(event)
  447.         send_event_to_systray_ui(event)
  448.         log.debug("Exiting")
  449.         main_loop.quit()
  450.  
  451.     elif event.event_code in (EVENT_DEVICE_START_POLLING,
  452.                               EVENT_DEVICE_STOP_POLLING):
  453.         pass
  454.  
  455.     else:
  456.         log.error("Unhandled event: %d" % event.event_code)
  457.  
  458.  
  459.  
  460. def send_systray_blip():
  461.     send_event_to_systray_ui(device.Event('', '', EVENT_DEVICE_UPDATE_BLIP))
  462.  
  463.  
  464. def send_event_to_systray_ui(event, event_code=None):
  465.     e = event.copy()
  466.  
  467.     if event_code is not None:
  468.         e.event_code = event_code
  469.  
  470.     e.send_via_pipe(w1, 'systemtray')
  471.  
  472.  
  473. def send_event_to_hpdio(event):
  474.     event.send_via_pipe(w2, 'hpdio')
  475.  
  476.  
  477. def send_toolbox_event(event, event_code=None):
  478.     global session_bus
  479.  
  480.     e = event.copy()
  481.  
  482.     if event_code is not None:
  483.         e.event_code = event_code
  484.  
  485.     e.send_via_dbus(session_bus, 'com.hplip.Toolbox')
  486.  
  487.  
  488.  
  489. def handle_signal(typ, *args, **kwds):
  490.     if kwds['interface'] == 'com.hplip.StatusService' and \
  491.         kwds['member'] == 'Event':
  492.  
  493.         event = device.Event(*args[:6])
  494.         return handle_event(event, args[6:])
  495.  
  496.  
  497. def handle_system_signal(*args, **kwds):
  498.     return handle_signal('system', *args, **kwds)
  499.  
  500.  
  501. def handle_session_signal(*args, **kwds):
  502.     return handle_signal('session', *args, **kwds)
  503.  
  504.  
  505.  
  506. def run(write_pipe1=None,  # write pipe to systemtray
  507.         write_pipe2=None,  # write pipe to hpdio
  508.         read_pipe3=None):  # read pipe from hpdio
  509.  
  510.     global dbus_loop, main_loop
  511.     global system_bus, session_bus
  512.     global w1, w2, r3
  513.  
  514.     log.set_module("hp-systray(hpssd)")
  515.     log.debug("PID=%d" % os.getpid())
  516.     w1, w2, r3 = write_pipe1, write_pipe2, read_pipe3
  517.  
  518.     dbus_loop = DBusGMainLoop(set_as_default=True)
  519.     main_loop = MainLoop()
  520.  
  521.     try:
  522.         system_bus = SystemBus(mainloop=dbus_loop)
  523.     except dbus.exceptions.DBusException, e:
  524.         log.error("Unable to connect to dbus system bus. Exiting.")
  525.         sys.exit(1)
  526.  
  527.     try:
  528.         session_bus = dbus.SessionBus()
  529.     except dbus.exceptions.DBusException, e:
  530.         if os.getuid() != 0:
  531.             log.error("Unable to connect to dbus session bus. Exiting.")
  532.             sys.exit(1)
  533.         else:
  534.             log.error("Unable to connect to dbus session bus (running as root?)")
  535.             sys.exit(1)
  536.  
  537.     # Receive events from the system bus
  538.     system_bus.add_signal_receiver(handle_system_signal, sender_keyword='sender',
  539.         destination_keyword='dest', interface_keyword='interface',
  540.         member_keyword='member', path_keyword='path')
  541.  
  542.     # Receive events from the session bus
  543.     session_bus.add_signal_receiver(handle_session_signal, sender_keyword='sender',
  544.         destination_keyword='dest', interface_keyword='interface',
  545.         member_keyword='member', path_keyword='path')
  546.  
  547.     # Export an object on the session bus
  548.     session_name = dbus.service.BusName("com.hplip.StatusService", session_bus)
  549.     status_service = StatusService(session_name, "/com/hplip/StatusService")
  550.  
  551.     log.debug("Entering main dbus loop...")
  552.     try:
  553.         main_loop.run()
  554.     except KeyboardInterrupt:
  555.         log.debug("Ctrl-C: Exiting...")
  556.  
  557.